Types  >  Namespaces  >  CustomAttributes

Namespaces

Introduction

In Delphi for Microsoftâ .NET, a unit is still the basic container for types. Microsoft's Common Language Runtime introduces another layer of organization called a namespace. In the .NET Framework, a namespace is a conceptual container of types In Delphi for .NET, a namespace is a container of Delphi units. The addition of namespaces gives Delphi the ability to access and extend classes in the .NET Framework.

 

Unlike traditional Delphi units, namespaces can be nested to form a containment hierarchy. Nested namespaces provide a way to organize identifiers and types, and are used to disambiguate types with the same name. Since they are a container for Delphi units, namespaces may also be used to differentiate between units of the same name, that reside in different packages.

 

For example, the class MyClass in MyNameSpace, is different from the class MyClass in YourNamespace. At runtime, the CLR always refers to classes and types by their fully qualified names: the assembly name, followed by the namespace that contains the type. The CLR itself has no concept or implementation of the namespace hierarchy; it is purely a notational convenience of the programming language.

Declaring Namespaces

In Delphi for .NET, a project file (program, library, or package) implicitly introduces its own namespace, called the project default namespace. A unit may be a member of the project default namespace, or it may explicitly declare itself to be a member of a different namespace. In either case, a unit declares its namespace membership in its Unit statement. For example, consider the following explicit namespace declaration:

 

      Unit MyCompany.MyWidgets.MyUnit;

 

First, notice that namespaces are separated by dots. Namespaces do not introduce new symbols for the identifiers between the dots; the dots are actually part of the unit name. The source file name for this example is MyCompany.MyWidgets.MyUnit.pas, and the DCU output file name is MyCompany.MyWidgets.MyUnit.dcu.

 

Second, notice that the dots imply the conceptual nesting, or containment, of one namespace within another. The example above declares the unit MyUnit to be a member of the MyWidgets namespace, which itself is contained in the MyCompany namespace. Again, it should be noted that this containment is for documentation purposes only.

 

A project default namespace declares a namespace for all of the units in the project. Consider the following declarations:

 

      Program MyCompany.MyDepartment.MyProgram;

      Library MyCompany.MyDepartment.MyLibs.MyLibrary;

      Package MyCompany.MyDepartment.MyPackages.MyPackage;

 

These statements establish the project default namespace for the program, library, and package, respectively. The namespace is determined by removing the rightmost identifier (and dot) from the declaration.

 

A unit that omits an explicit namespace is called generic unit. A generic unit automatically becomes a member of the project default namespace. Given the preceding Program declaration, the following unit declaration would cause the compiler to treat MyUnit as a member of the MyCompany.MyDepartment namespace.

 

      Unit MyUnit;

 

The project default namespace does not affect the name of the Delphi source file for a generic unit. In the preceding example, the Delphi source file name would be MyUnit.pas. The compiler does however prefix the DCU file name with the project default namespace. The resulting DCU file in the current example would be MyCompany.MyDepartment.MyUnit.dcu.

 

Namespace strings are not case-sensitive. The compiler considers two namespaces that differ only in case to be equivalent. However, the compiler does preserve the case of a namespace, and will use the preserved casing in output file names, error messages, and RTTI unit identifiers. RTTI for class and type names will include the full namespace specification.

Searching Namespaces

A unit in Delphi for .NET must still declare the other units on which it depends. The compiler must search these units for identifiers, the same as always. For units in explicit namespaces the search scope is already known, but for generic units, the compiler must establish a namespace search scope.

 

Consider the following Unit and Uses declarations:

 

      Unit MyCompany.MyDepartment.MyUnits.MyUnit1;

      Uses MyCompany.OtherDepartment.TheirUnit, UnitX, UnitY;

 

These declarations establish MyUnit1 as a member of the MyCompany.MyDepartment.MyUnits namespace. MyUnit1 depends on three other units, MyCompany.OtherDepartment.TheirUnit, UnitX, and UnitY. The compiler can easily resolve identifier names in TheirUnit, since the Uses clause tells it exactly where to look. But how should the compiler resolve identifier names in UnitX and UnitY?

 

Search locations can come from four possible sources: Compiler options, the project file's Namespaces clause, the project default namespace, and finally, the current unit's namespace.

 

A project file (program, library or package) may optionally specify a list of namespaces to be searched when resolving generic unit names. The Namespaces clause must appear in the project file, immediately after the Program, Library, or Package declaration and before any other clause or block type. The Namespaces clause is a list of namespace identifiers, separated by commas. A semicolon must terminate the list of namespaces. The following example establishes a Program and namespace search list:

 

      Program MyCompany.MyDepartment.MyProgram;

      Namespaces MyCompany.OtherDepartment.TheirWidgets, MyCompany.MyOtherNamespace;

 

The compiler resolves identifier names in the following order:

 

  1. The current unit namespace (if any)
  2. The project default namespace (if any)
  3. Namespaces specified by the project's Namespaces clause (searched in reverse order of declaration in the list).
  4. Namespaces specified by compiler options.

 

Continuing with the example above, the compiler would use the following search scope when resolving identifier names in UnitX and UnitY:

 

  1. MyCompany.MyDepartment.MyUnits
  2. MyCompany.MyDepartment
  3. MyCompany.MyOtherNamespace
  4. MyCompany.OtherDepartment.TheirWidgets
  5. Namespaces specified by compiler options.

 

Note that if the current unit is generic (i.e. it does not have an explicit namespace declaration in its Unit statement), then resolution begins with the project default namespace.

Using Namespaces

Delphi's Uses clause brings a module into the context of the current unit. The Uses clause must either refer to a module by its fully qualified name (i.e. including the full namespaces specification), or by its generic name, thereby relying on the namespace resolution mechanisms to locate the unit. The following example demonstrates the Uses clause with namespaces:

 

      Unit MyCompany.MyDepartment.MyUnits.MyUnit1

      Uses MyCompany.MyDepartment.OtherUnit,       // Fully qualified name.

            UnitX;                              // Generic name.

 

Once a module has been brought into context, source code can refer to identifiers within that module either by the unqualified name, or by the fully qualified name (if necessary, to disambiguate identifiers with the same name in different units). The following writeln statements are equivalent:

 

      Uses MyCompany.MyDepartment.OtherUnit;

 

      begin

            writeln(MyCompany.MyDepartment.OtherUnit.SomeString);

            writeln(SomeString);

      end.

 

A fully qualified identifier must include the full namespace specification. In the preceding example, it would be an error to refer to SomeString using only a portion of the namespace:

 

      writeln(OtherUnit.SomeString);                        // ERROR!

      writeln(MyDepartment.OtherUnit.SomeString);           // ERROR!

      writeln(MyCompany.MyDepartment.OtherUnit.SomeString);      // Correct.

      writeln(SomeString);                                  // Correct.

 

It is also an error to refer to only a portion of a namespace in the Uses clause. There is no mechanism to import all units and symbols in a namespace. The following code does not import all symbols in the MyCompany namespace:

 

      Uses MyCompany;              // ERROR!

 

This restriction also applies to the with-do statement. The following will produce a compiler error:

 

      with MyCompany.MyDepartment do    // ERROR!

 

 

      &begin('a string');

 

Unit Aliases

Namespaces can become quite long and cumbersome to type. A new feature of the Delphi language allows you to introduce an alias for a longwinded namespace. The following Uses clause introduces a local unit alias:

 

      Uses MyCompany.MyDepartment.OtherUnit as aUnit;

 

Given a unit alias, your source code can refer to an identifier by the shorter name:

 

      // These two statements are equivalent.

      writeln(aUnit.SomeString);

      writeln(MyCompany.MyDepartment.OtherUnit.SomeString);

 

Unit aliases must not conflict with other unit or namespace identifiers. Unit aliases introduce Delphi identifiers, and therefore cannot contain dots. A unit alias is local to the unit in which it is declared. A unit alias does not obscure the fully qualified unit name that it aliases; you may always refer to an identifier either by it's fully qualified name, or by a unit alias.


Top  |  Contents  |  Copyrights  |  Borland Developer Network  |  Borland Home